package org.leumasjaffe.io;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Scanner;

import org.eclipse.jdt.annotation.NonNull;

import static org.leumasjaffe.io.PortablePrinter.*;

/**
 * Class PortableScanner in file PortableScanner.java
 * 
 * @author leumasjaffe
 * Created on: Jul 26, 2012
 * 
 */
public final class PortableScanner {
	/**
	 * 
	 */
	private static final HashMap<String, Scanner> scanners = new HashMap<String, Scanner>();
	/**
	 * 
	 */
	private static Scanner keys;
	
	/**
	 * 
	 */
	private static final Scanner stdin = new Scanner(System.in);
	
	static {
		scanners.put("STDIN", stdin);
		setScanner(stdin);
	}
		
	/**
	 * 
	 */
	private PortableScanner() {}
		
	/**
	 * @param scanner
	 */
	private static void setScanner(@NonNull Scanner scanner) {
		keys = scanner;
	}
	
	/**
	 * @param name
	 * @param source
	 * @return
	 */
	public static boolean createScanner(@NonNull String name, @NonNull InputStream source) {
		if (scanners.get(name) != null) return false;
		scanners.put(name, new Scanner(source));
		return true;
	}
	
	/**
	 * @param name
	 * @return
	 */
	public static boolean setScanner(@NonNull String name) {
		Scanner scan = scanners.get(name);
		if (scan == null) return false;
		setScanner(scan);
		return true;
	}
	
	/**
	 * @return
	 */
	public static boolean setScanner() {
		setScanner(stdin);
		return true;
	}

	/**
	 * @return
	 */
	public static int nextInt() {
		while (true) {
			if (keys.hasNextInt()) { //If there was a number input
				return keys.nextInt(); //Return the integer
			}
			else {
				println("Error: NaN");
				//Else warn the user and re-print the message
				keys.nextLine(); //Next-line the input to flush
			}
		}
	}
	
	/**
	 * @param message
	 * @return
	 */
	public static int nextInt(@NonNull String message) {
		print(message, ": "); //Let the user know what we're looking for
		while (true) {
			if (keys.hasNextInt()) { //If there was a number input
				return keys.nextInt(); //Return the integer
			}
			else {
				print("Error: NaN\n", message, ": ");
				//Else warn the user and re-print the message
				keys.nextLine(); //Next-line the input to flush
			}
		}
	}
	
	/**
	 * @param min_inc
	 * @param max_exc
	 * @return
	 */
	public static int nextInt(int min_inc, int max_exc) {
		int out;
		while (true) {
			if (keys.hasNextInt()) { //If there was a number input
				out = keys.nextInt(); 
				if (out >= min_inc && out < max_exc)
					return out;//Return the integer
				else {
					println("Error: Number is not in the range [", min_inc, ",", max_exc, ")");
					//Else warn the user and re-print the message
					keys.nextLine(); //Next-line the input to flush
				}
			}
			else {
				println("Error: NaN");
				//Else warn the user and re-print the message
				keys.nextLine(); //Next-line the input to flush
			}
		}
	}
	
	/**
	 * @param message
	 * @param min_inc
	 * @param max_exc
	 * @return
	 */
	public static int nextInt(@NonNull String message, int min_inc, int max_exc) {
		print(message, ": "); //Let the user know what we're looking for
		int out;
		while (true) {
			if (keys.hasNextInt()) { //If there was a number input
				out = keys.nextInt(); 
				if (out >= min_inc && out < max_exc)
					return out;//Return the integer
				else {
					print("Error: Number is not in the range [", min_inc, ",", max_exc, ")", "\n",
							message, ": ");
					//Else warn the user and re-print the message
					keys.nextLine(); //Next-line the input to flush
				}
			}
			else {
				print("Error: NaN", "\n", message, ": ");
				//Else warn the user and re-print the message
				keys.nextLine(); //Next-line the input to flush
			}
		}
	}
	
	/**
	 * @return
	 */
	public static double nextFloat() {
		while (true) {
			if (keys.hasNextFloat()) { //If there was a number input
				return keys.nextFloat(); //Return the integer
			}
			else {
				println("Error: NaN");
				//Else warn the user and re-print the message
				keys.nextLine(); //Next-line the input to flush
			}
		}
	}

	/**
	 * @param message
	 * @return
	 */
	public static double nextFloat(@NonNull String message) {
		print(message, ": "); //Let the user know what we're looking for
		while (true) {
			if (keys.hasNextFloat()) { //If there was a number input
				return keys.nextFloat(); //Return the integer
			}
			else {
				print("Error: NaN", "\n", message, ": ");
				//Else warn the user and re-print the message
				keys.nextLine(); //Next-line the input to flush
			}
		}
	}
	
	/**
	 * @param min_inc
	 * @param max_exc
	 * @return
	 */
	public static double nextFloat(int min_inc, int max_exc) {
		double out;
		while (true) {
			if (keys.hasNextFloat()) { //If there was a number input
				out = keys.nextFloat(); 
				if (out >= min_inc && out < max_exc)
					return out;//Return the integer
				else {
					println("Error: Number is not in the range [", min_inc, ",", max_exc, ")");
					//Else warn the user and re-print the message
					keys.nextLine(); //Next-line the input to flush
				}
			}
			else {
				println("Error: NaN");
				//Else warn the user and re-print the message
				keys.nextLine(); //Next-line the input to flush
			}
		}
	}
	
	/**
	 * @param message
	 * @param min_inc
	 * @param max_exc
	 * @return
	 */
	public static double nextFloat(@NonNull String message, int min_inc, int max_exc) {
		print(message, ": "); //Let the user know what we're looking for
		double out;
		while (true) {
			if (keys.hasNextFloat()) { //If there was a number input
				out = keys.nextFloat(); 
				if (out >= min_inc && out < max_exc)
					return out;//Return the integer
				else {
					print("Error: Number is not in the range [", min_inc, ",", max_exc, ")", "\n",
							message, ": ");
					//Else warn the user and re-print the message
					keys.nextLine(); //Next-line the input to flush
				}
			}
			else {
				print("Error: NaN", "\n", message, ": ");
				//Else warn the user and re-print the message
				keys.nextLine(); //Next-line the input to flush
			}
		}
	}
	
	/**
	 * @return
	 */
	public static double nextDouble() {
		while (true) {
			if (keys.hasNextDouble()) { //If there was a number input
				return keys.nextDouble(); //Return the integer
			}
			else {
				println("Error: NaN");
				//Else warn the user and re-print the message
				keys.nextLine(); //Next-line the input to flush
			}
		}
	}

	/**
	 * @param message
	 * @return
	 */
	public static double nextDouble(@NonNull String message) {
		print(message, ": "); //Let the user know what we're looking for
		while (true) {
			if (keys.hasNextDouble()) { //If there was a number input
				return keys.nextDouble(); //Return the integer
			}
			else {
				print("Error: NaN", "\n", message, ": ");
				//Else warn the user and re-print the message
				keys.nextLine(); //Next-line the input to flush
			}
		}
	}
	
	/**
	 * @param min_inc
	 * @param max_exc
	 * @return
	 */
	public static double nextDouble(int min_inc, int max_exc) {
		double out;
		while (true) {
			if (keys.hasNextDouble()) { //If there was a number input
				out = keys.nextDouble(); 
				if (out >= min_inc && out < max_exc)
					return out;//Return the integer
				else {
					println("Error: Number is not in the range [", min_inc, ",", max_exc, ")");
					//Else warn the user and re-print the message
					keys.nextLine(); //Next-line the input to flush
				}
			}
			else {
				println("Error: NaN");
				//Else warn the user and re-print the message
				keys.nextLine(); //Next-line the input to flush
			}
		}
	}
	
	/**
	 * @param message
	 * @param min_inc
	 * @param max_exc
	 * @return
	 */
	public static double nextDouble(@NonNull String message, int min_inc, int max_exc) {
		print(message, ": "); //Let the user know what we're looking for
		double out;
		while (true) {
			if (keys.hasNextDouble()) { //If there was a number input
				out = keys.nextDouble(); 
				if (out >= min_inc && out < max_exc)
					return out;//Return the integer
				else {
					print("Error: Number is not in the range [", min_inc, ",", max_exc, ")", "\n",
							message, ": ");
					//Else warn the user and re-print the message
					keys.nextLine(); //Next-line the input to flush
				}
			}
			else {
				print("Error: NaN", "\n", message, ": ");
				//Else warn the user and re-print the message
				keys.nextLine(); //Next-line the input to flush
			}
		}
	}
	
	/**
	 * @return
	 */
	public static String nextLine() {
		return keys.nextLine();
	}
	
	/**
	 * @param prompt
	 * @return
	 */
	public static String nextLine(@NonNull String prompt) {
		print(prompt, ": ");
		return keys.nextLine();
	}

}
